#include "RemoteClient.h"
#include "../logger/Logger.h"

#include <QMutexLocker>

#include <iostream>

namespace Network {

RemoteClient::RemoteClient(QTcpSocket * sock, int id, QObject * parent) : QThread(parent), in_queue_mutex(1), out_queue_mutex(1) {

	sock_ = sock;
	id_ = id;
	header_found_ = false;
	//packet = NULL;

	addr_ = sock_->peerAddress();
	port_ = sock_->peerPort();

	msleep(100);
}

QString RemoteClient::getAddr() {
	if (sock_->state() == QAbstractSocket::ConnectedState)
		return addr_.toString() + ":" + QString::number(port_);
	else
		return QString("N/C");
}

void RemoteClient::run() {
//	out_queue_mutex = new QMutex;
//	in_queue_mutex = new QMutex;

	active_ = true;
	while (active_) {
		if (sock_->state() == QAbstractSocket::UnconnectedState) {
			onDisconnected();
			break;
		}

		if (out_queue.size() > 0)
			onSend();

		if (sock_->bytesAvailable())
			onNewData();


		msleep(10);
	}

//	delete out_queue_mutex;
//	delete in_queue_mutex;
}

RemoteClient::~RemoteClient() {
	//delete sock_;
}

void RemoteClient::stop() {
	active_ = false;
}

void RemoteClient::send(Packet * packet) {
	ScopedWriter sw("RemoteClient::send()", false);

	// jeśli pakiet jest wysyłany przez klienta, który nie jest jeszcze zarejestrowany
	// i nie jest to pakiet ptIDRequest, to odrzucamy go z kolejki

	if ( (packet->client_id == 0) && (packet->id != Packet::ptIDRequest) ) {
		//emit newMsg("Klient nie jest jeszcze zarejestrowany!", Logger::mtError);

		delete packet;
		return;
	}

	//sw << packet->dump().toStdString() << "\n";
	//packet->pack();

	out_queue_mutex.p();
	out_queue.enqueue(packet);
	out_queue_mutex.v();
}

Packet * RemoteClient::recv() {
//	QMutexLocker locker(in_queue_mutex);
	Packet * packet;
	in_queue_mutex.p();

	if (in_queue.isEmpty())
		packet = NULL;
	else
		packet =  in_queue.dequeue();

	in_queue_mutex.v();
	return packet;
}

void RemoteClient::onSend() {
	// rozmiar kolejki
	int size;
	// pakiet tymczasowy
	Packet * packet;
	// ilość wysłanych bajtów
	int res;
	int cnt;

	ScopedWriter sw("RemoteClient::onSend()", false);

	// sprawdzenie aktualnego rozmiaru kolejki
	out_queue_mutex.p();
	size = out_queue.size();
	out_queue_mutex.v();

	sw << "Długość kolejki: " << size << "\n";

	// wysyłamy określoną ilość pakietów
	for (int i = 0; i < size; ++i) {
		// wyciągnięcie pakietu z kolejki

		char * buf;
		int bufsize;

		out_queue_mutex.p();
		packet = out_queue.dequeue();
		out_queue_mutex.v();

		if (packet == NULL)
			continue;

		//sw << packet->dump().toStdString() << "\n";

		// wysłanie pakietu

		bufsize = packet->size;
		buf = new char[bufsize];

		*((int*)(buf)) = packet->id;
		*((int*)(buf+4)) = packet->client_id;
		*((int*)(buf+8)) = packet->size;

		for (int i = 12; i < bufsize; ++i)
			buf[i] = packet->data()[i-12];

		delete packet;
		packet = NULL;

		// wysyłanie pakietu

		cnt = 0;

		while (cnt < bufsize) {

			res = sock_->write(buf + cnt, qMin(bufsize - cnt, 1000));

			// błąd zapisu - połączenie zdechło
			if (res < 0) {
				delete [] buf;
				emit clientDied(this);
				return;
			}
			cnt += res;
			sw << cnt << "\n";
		}

		delete [] buf;

/*
		std::cout << "Stan gniazda: " << sock_->errorString().toStdString() << "\n";

		std::cout << "Wysłano do sieci pakiet: " << packet->dump().toStdString();
		std::cout.flush();
*/

		// ostateczne zwolnienie pamięci zajmowanej przez pakiet
		//delete packet;
	}

	sock_->flush();
}

void RemoteClient::onDisconnected() {
	emit clientDied(this);
}

void RemoteClient::onNewData() {
	// odbierany pakiet
	Packet * packet;
	ScopedWriter sw("RemoteClient::onNewData()", false);


	// ilość odczytanych bajtów
	int res = 0;
	int cnt = 0;

	if (!header_found_) {
		sw << "Odbieranie nagłówka\n";

		res = 0;

		if (sock_->bytesAvailable() >= 12) {
			packet = new Packet;
			while (cnt < 12) {
				res = sock_->read(((char*)packet) + cnt, 12 - cnt);
				// błąd odczytu, prawdopodobnie połączenie z klientem zostało utracone
				// więc należy je usunąć z listy aktywnych
				if (res < 0) {
					emit clientDied(this);
					return;
				}
				cnt += res;
			}
			sw << "Rozmiar pakietu: " << packet->size << "\n";

			// jakiś dziwny błąd, nagłówek nei ma prawa tak wyglądać
			if ( (packet->size < 12) || (packet->id < 0) || (packet->client_id < 0) ) {
				delete packet;
				return;
			}


			header_found_ = true;
		} else
			return;
	}

	cnt = 0;
	res = 0;
	if (header_found_) {
		sw << "Odbieranie ciała pakietu\n";
		int body_size = packet->size - 12;

		char * buf = new char[body_size];
		sw << "W gnieździe siedzi " << sock_->bytesAvailable() << " bajtów.\n";
		if ( (sock_->bytesAvailable()) || (sock_->bytesAvailable() == body_size) ) {
			while (cnt < body_size) {
				res = sock_->read(buf + cnt, qMin(body_size - cnt, 1000));
				if (res > 0)
					sw << "Odczytano " << res << " bajtów.\n";
				// błąd odczytu, prawdopodobnie połączenie z klientem zostało utracone
				// więc należy je usunąć z listy aktywnych
				if (res < 0) {
					emit clientDied(this);
					delete [] buf;
					return;
				}
				cnt += res;
			}

			packet->data(buf, body_size);
			delete [] buf;


			sw << "Odebrano pakiet. Wrzucam do kolejki...\n";
			// odebrano cały pakiet
			// wstawienie do kolejki odebranych
			in_queue_mutex.p();
			in_queue.enqueue(packet);
			in_queue_mutex.v();
			sw << "Pakiet siedzi w kolejce\n";

			// wysyłamy informację o odebraniu pakietu
			emit newPacket(this);


			//sw << "Odebrany pakiet: " << packet->dump().toStdString();


			// zaczynamu poszukiwania od nowa
			header_found_ = false;
		}
	}
}

} // namespace Network
